﻿using System;
using System.Threading;
using System.ComponentModel;
using System.Collections.Generic;

using SBPweb.Forge.Common.Patterns;

using SBPweb.Forge.Contracts.Logging;

using SBPweb.Forge.Logger.Actors;
using SBPweb.Forge.Logger.Writers;

namespace SBPweb.Forge.Logger
{

    public class LogManager : LogManagerBase
    {

        #region Private fields

        private Configuration.LogSettingsConfigurationSection config;
        private LockHelper lh = new LockHelper("LogManager");
        private Dictionary<string, List<string>> categories = new Dictionary<string, List<string>>();
        private Dictionary<string, LogListener> listeners = new Dictionary<string, LogListener>();

        #endregion

        #region Constructor logic

        public LogManager()
            : base()
        {
            InitializeConfiguration();
        }

        #endregion

        #region Property accessors

        [ReadOnly(true)]
        public override ICollection<string> Categories
        {
            get
            {
                return categories.Keys;
            }
        }

        [ReadOnly(true)]
        public ICollection<string> Listeners
        {
            get
            {
                return listeners.Keys;
            }
        }

        #endregion

        #region Methods

        #region Initialization - termination

        /// <summary>
        /// Initialize all listeners with separated threads
        /// </summary>
        protected override void OnInitialize()
        {
            ThreadPool.SetMinThreads(listeners.Count, listeners.Count);
            foreach (LogListener listener in listeners.Values)
            {
                ThreadPool.QueueUserWorkItem(InitializeListeners, listener);
            }
        }

        /// <summary>
        /// Initialize listeners you must call from 
        /// </summary>
        private void InitializeListeners(Object listener)
        {
            (listener as LogListener).StartWriting();
        }

        /// <summary>
        /// Stopping all listeners
        /// </summary>
        protected override void OnTerminate()
        {
            foreach (LogListener listener in listeners.Values)
            {
                listener.EndWriting();
            }
        }

        /// <summary>
        /// Read listeners and categories from config file
        /// </summary>
        private void InitializeConfiguration()
        {
            config = Configuration.LogSettingsConfigurationSection.OpenSectionInstance();
            if (config == null)
            {
                config = new SBPweb.Forge.Logger.Configuration.LogSettingsConfigurationSection();
            }

            using (lh.GetLock())
            {
                listeners.Clear();
                foreach (Configuration.ListenerElement listener in config.Listeners)
                {
                    listeners.Add(listener.Name, new LogListener(listener));
                }

                categories.Clear();
                foreach (Configuration.CategoryElement category in config.Categories)
                {
                    List<string> listenerNames = new List<string>(category.ListenerNames.AllKeys);
                    foreach(string name in listenerNames)
                    {
                        if (!listeners.ContainsKey(name))
                        {
                            throw new Exception(string.Format("Listener {0} not found for category {1}!", name, category.Name));
                        }
                    }
                    categories.Add(category.Name, listenerNames);
                }
            }
        }

        #endregion

        protected override void OnWrite(object sender, SeverityLevel severity, object obj, string category)
        {
            if (categories.ContainsKey(category))
            {
                foreach (string listener in categories[category])
                {
                    listeners[listener].EnqueueItem(new LogItem(sender, severity, obj, category));
                }
            }
            base.OnWrite(sender, severity, obj, category);
        }
        
        #endregion

    }

}